import pygame,pygame.locals
import sys,os,time

#   2021-05-23  18:08   by  炸天帮成员
#   昨天下午袁隆平老爷爷去山里了，91岁高龄也不容易。
#   关于本文件的迭代，可以查看_8_的所有版本的最后的总结。
#   在_8_pygame_demo_color_dictWithClass_1_ExOK.py的基础上，
#   添加了对每次调用move()的时间间隔的操作，即：
#   名为 GREEN_BLOCK_DICT['x']的绿色方块 ，在 BLOCK_CREATE_TIME_LIST[x] 秒 时出现在 
#   pygame窗口的 self.START_POSITION 位置，并以
#   每隔 self.GREEN_BLOCK_ONESTEP_MOVE_TIME 秒 移动 self.MOVE_STEP 像素步长的速度，往左移动。
#   共计GREEN_BLOCK_MAX_NUMBER个绿色小方块。
#   注意标记为[S0]的参数都是可以修改的。

def game_init():
    pygame.init()
    pygame.display.init()

def refresh_pygame():                   #   这个refresh_pygame()函数要和 pygame.display.update() 一起结合使用。
                                        #   这样程序才不会运行至一半导致崩溃，另外仅有move_ip()而无blit()，也会导致图片不会刷新。
    FPS = 60                            #[S0]
    pygame.time.Clock().tick(FPS)
    for event in pygame.event.get():
        if event.type == pygame.locals.QUIT:
            pygame.quit()
            sys.exit()
    DISPLAYSURF.blit(BKG_IMAGE,(0,0))   #   即背景图片的图层最先刷新，因为后出的blit()加载图片 会覆盖上一个blit()加载图片
                                        #   如果将此处注释掉 ， 则 那几个 IRO_BLOCK 中的 move() 会产生拖影。
                                        #   如果将背景图片的刷新给放入IRO_BLOCK的load()中，就会出现只显示 最后的blit()加载图片
COLOR_BLACK = (0,0,0)
COLOR_WHITE = (255,255,255)
COLOR_BKG = COLOR_BLACK

WORKPLACE_PATH = os.getcwd()
IMAGE_PATH = f'{WORKPLACE_PATH}\COLOR_BLOCK_PNG\\'  #[S0]

WINDOW_X_MAX = 400                                  #[S0]
WINDOW_Y_MAX = 400                                  #[S0]
DISPLAYSURF = pygame.display.set_mode((WINDOW_X_MAX,WINDOW_Y_MAX))
DISPLAYSURF.fill(COLOR_BKG)
BKG_IMAGE = pygame.image.load(IMAGE_PATH + 'whiteBKG.png')  #[S0]

GREEN_BLOCK_DICT = {            #   原始绿色方块字典的样式
    '0':{
        'ID':0,
        'TYPE':'GREEN_BLOCK',
        'NAME':'GREEN_BLOCK_0',
        'ALIVE_STATUS':False,
        'X_SIZE':40,
        'Y_SIZE':40,
        'START_POSITION':(WINDOW_X_MAX,WINDOW_Y_MAX/2),
        'MOVE_STEP':-5,
        'IMAGE':pygame.image.load(IMAGE_PATH + 'greenBlock.png'),   #[S0]
        'IRO_BLOCK_LOAD_STATE':False,    #   当CREATE_JIKANN参数不为None时，用于判断IRO_BLOCK 这个CLASS中的blit()是否正常被调用了。若正常被调用，则设置为True。
        'CREATE_JIKANN':None,            #   即绿色方块出现的时间点，该时间点相对于PYGAME_START_TIME，单位为s。为None时，即从程序开始运行就出现（即理论上等同于值为0，但实际上还是有时间差的）。
    }
}

class IRO_BLOCK(pygame.sprite.Sprite):
    def __init__(self,BLOCK_ID,BLOCK_TYPE,BLOCK_NAME,\
                      BLOCK_ALIVE_STATUS,BLOCK_X_SIZE,BLOCK_Y_SIZE,\
                      BLOCK_START_POSITION,BLOCK_MOVE_STEP,BLOCK_IMAGE,\
                      BL_IRO_BLOCK_LOAD_STATE = False,BLOCK_CREATE_JIKANN = None):
        super().__init__()
        self.ID = BLOCK_ID
        self.TYPE = BLOCK_TYPE
        self.NAME = BLOCK_NAME
        self.ALIVE_STATUS = BLOCK_ALIVE_STATUS
        self.X_SIZE = BLOCK_X_SIZE
        self.Y_SIZE = BLOCK_Y_SIZE
        self.START_POSITION = BLOCK_START_POSITION
        self.MOVE_STEP = BLOCK_MOVE_STEP
        self.IMAGE = BLOCK_IMAGE
        self.IRO_BLOCK_LOAD_STATE = BL_IRO_BLOCK_LOAD_STATE     #   用于控制 显示的同时并移动的 标志位。True 即为 显示的同时并移动pygame.Rect对象；False 即为报错，未加载图片。当然False也可以改为 不显示（隐身）的同时并移动pygame.Rect对象，见[F0]。
        self.CREATE_JIKANN = BLOCK_CREATE_JIKANN                #   即绿色方块出现的时间点，该时间点相对于PYGAME_START_TIME，单位为s。为None时，即从程序开始运行就出现（即理论上等同于值为0，但实际上还是有时间差的）。
        print('self.CREATE_JIKANN = ',self.CREATE_JIKANN)
        self.LAST_DISPLAY_JIKANN = None                         #   即该ID对应的绿色小方块（存活状态下）上次移动结束的时间点。None即意味着所有该时间均为CLASS中move()被调用完时自动计算。
        
        self.SURF = pygame.Surface((self.X_SIZE,self.Y_SIZE))
        self.RECT = self.SURF.get_rect(center = self.START_POSITION)

    def load(self):
        global PYGAME_START_TIME
        if self.CREATE_JIKANN == None :
            DISPLAYSURF.blit(self.IMAGE,self.RECT)
            self.IRO_BLOCK_LOAD_STATE = True    #   self.IRO_BLOCK_LOAD_STATE 用于控制 move()   中的pygame.Rect是否真的需要 同时显示并移动。
        else :
            #   下面这个 if 用于在 规定的时间内 第一次显示 绿色方块。
            if self.CREATE_JIKANN < time.time() - PYGAME_START_TIME < self.CREATE_JIKANN + 1 and self.IRO_BLOCK_LOAD_STATE == False :   #[T0]   time.time() - PYGAME_START_TIME 需要一个范围值才能被使用，因为计算结果本身是个浮点数。
                                                                                                                                        #   上一行即意味着在1s内首次在pygame的屏幕上显示 绿色方块。
                DISPLAYSURF.blit(self.IMAGE,self.RECT)
                self.IRO_BLOCK_LOAD_STATE = True                                                                                        #   将IRO_BLOCK_LOAD_STATE  从 False 改变为 True,即意味着move()函数将变为使用有效。
                print(f'绿色方块{self.NAME}刚出现时的时间 : ',time.ctime(time.time()))
                print(f'绿色方块{self.NAME}刚出现时的self.RECT.left = ',self.RECT.left)
            #   下面这个elif    意味着即当该pygame.Rect依旧存活时，显示pygame.Rect对象
            elif self.IRO_BLOCK_LOAD_STATE == True :
                DISPLAYSURF.blit(self.IMAGE,self.RECT)
                print(f'绿色方块{self.NAME}的时间 : ',time.ctime(time.time()))
                print(f'绿色方块{self.NAME}的self.RECT.left = ',self.RECT.left)

    def move(self):
        global GREEN_BLOCK_ONESTEP_MOVE_TIME
        if self.IRO_BLOCK_LOAD_STATE == True :  #   即  load()函数已被调用，需要 同时显示pygame.Rect对象并移动该pygame.Rect对象。
            if self.LAST_DISPLAY_JIKANN == None :   #   当生成pygame中的第一个方块时。
                self.RECT.move_ip(self.MOVE_STEP,0)
                if self.RECT.right < 0 :        #   可以从 self.ALIVE_STATUS 和 self.IRO_BLOCK_LOAD_STATE 的赋值状态可以看出，本文件中，这两个状态值的含义是差不多的，即都代表着 需要显示并移动的 绿色方块 的 生存状态。
                                                #   只不过 本文件中使用 self.IRO_BLOCK_LOAD_STATE 来控制 load() 和 move() 的状态。
                    self.ALIVE_STATUS = False   #   如果绿色方块移出pygame的窗口，则设定该绿色方块已死亡，一切关于该方块的标志位均要回归 初始状态。
                    self.IRO_BLOCK_LOAD_STATE = False   #   绿色方块死亡，即意味着load()函数的标志位 回归 False
                    #self.LAST_DISPLAY_JIKANN = None    #   可以开启本行将self.LAST_DISPLAY_JIKANN回归 None，但不开启的话，就可以查看到 pygame.Rect移出Pygame窗口前的时间。
                else :
                    self.ALIVE_STATUS = True    #   如果绿色方块未移出pygame的窗口，则设定该绿色方块存活。
                    self.IRO_BLOCK_LOAD_STATE = True    #   绿色方块存活，即意味着load()函数的标志位 保持 True
                    self.LAST_DISPLAY_JIKANN = time.time()
            else :
                if GREEN_BLOCK_ONESTEP_MOVE_TIME - 0.1 < time.time() - self.LAST_DISPLAY_JIKANN < GREEN_BLOCK_ONESTEP_MOVE_TIME :    #[S0]   即规定每隔 GREEN_BLOCK_ONESTEP_MOVE_TIME 秒 移动1次(即GREEN_BLOCK_ONESTEP_MOVE_TIME s 调用一次move()),
                                                                                                                                     #[T1]       移动长度为self.MOVE_STEP。和[T0]一样，都是一个范围值，所以如果想要 每次移动间隔时间为1s时，就要为0.9~1.0
                    self.RECT.move_ip(self.MOVE_STEP,0)
                    if self.RECT.right < 0 :        #   可以从 self.ALIVE_STATUS 和 self.IRO_BLOCK_LOAD_STATE 的赋值状态可以看出，本文件中，这两个状态值的含义是差不多的，即都代表着 需要显示并移动的 绿色方块 的 生存状态。
                                                    #   只不过 本文件中使用 self.IRO_BLOCK_LOAD_STATE 来控制 load() 和 move() 的状态。
                        self.ALIVE_STATUS = False   #   如果绿色方块移出pygame的窗口，则设定该绿色方块已死亡，一切关于该方块的标志位均要回归 初始状态。
                        self.IRO_BLOCK_LOAD_STATE = False   #   绿色方块死亡，即意味着load()函数的标志位 回归 False
                        #self.LAST_DISPLAY_JIKANN = None    #   可以开启本行将self.LAST_DISPLAY_JIKANN回归 None，但不开启的话，就可以查看到 pygame.Rect移出Pygame窗口前的时间。
                    else :
                        self.ALIVE_STATUS = True    #   如果绿色方块未移出pygame的窗口，则设定该绿色方块存活。
                        self.IRO_BLOCK_LOAD_STATE = True    #   绿色方块存活，即意味着load()函数的标志位 保持 True
                        self.LAST_DISPLAY_JIKANN = time.time()
        else :
            print(f'{self.NAME}图像未被{self.NAME}.load()!')
            #self.RECT.move_ip(self.MOVE_STEP,0)                                #[F0]    若开启这行，则意味着，需要 不显示（隐身）pygame.Rect对象的同时 移动该pygame.Rect对象。即不显示pygame.Rect对象的时候，该pygame.Rect对象也会发生移动。
                                                                                #[F0]    需要再做修改才能满足上一行的这句话，因为设定self.CREATE_JIKANN后，一开始pygame.Rect对象由于[F0]开启，也是在移动的，所以导致在需要显示该pygame.Rect对象的时刻时，
                                                                                #[F0]    该pygame.Rect对象已经被移出pygame的窗口了(从Terminal的运行结果可以看出来)，所以可以通过加上time限制或pygame.Rect.left或者right的限定或者添加load()被调用的次数
                                                                                #[F0]    来实现该 隐身的同时并移动的 函数功能。
            #print('self.IRO_BLOCK_LOAD_STATE = ',self.IRO_BLOCK_LOAD_STATE)    #[F1]    若关闭[F0], 则意味着当前程序只在 pygame.Rect对象显示的时候，该pygame.Rect对象才会发生移动。

# 以下4行用于测试   将字典中的条目  印射到  IRO_BLOCK   这个    CLASS中，并实例化(对象OBJECT化)
# GREEN_BLOCK_DICT['0'] = IRO_BLOCK(GREEN_BLOCK_DICT['0']['ID'],GREEN_BLOCK_DICT['0']['TYPE'],GREEN_BLOCK_DICT['0']['NAME'],\
#                     GREEN_BLOCK_DICT['0']['ALIVE_STATUS'],GREEN_BLOCK_DICT['0']['X_SIZE'],GREEN_BLOCK_DICT['0']['Y_SIZE'],\
#                     GREEN_BLOCK_DICT['0']['START_POSITION'],GREEN_BLOCK_DICT['0']['MOVE_STEP'],GREEN_BLOCK_DICT['0']['IMAGE'])
# print("GREEN_BLOCK_DICT['0'] = ",GREEN_BLOCK_DICT['0'],'\n类型为 ： ',type(GREEN_BLOCK_DICT['0']))

GREEN_BLOCK_MAX_NUMBER = 3                  #[S0]   设置 最多有  GREEN_BLOCK_MAX_NUMBER  个绿色方块。使用时从下标0开始。
BLOCK_CREATE_TIME_LIST = [2,16,22]          #[S0]   即每个  绿色小方块出现的时间点，可以为浮点数，单位为s.不要超过 SET_PYGAME_RUNNING_TIME 的时长。
GREEN_BLOCK_ONESTEP_MOVE_TIME = 1           #[S0]   设定 每次移动 需要的 间隔 时间，单位为 s
def NEW_GREEN_BLOCK_OBJ(BLOCK_MAX_NUMBER):
    for i in range (0,GREEN_BLOCK_MAX_NUMBER):
        GREEN_BLOCK_DICT[str(i)] = {
                                    'ID':i,
                                    'TYPE':'GREEN_BLOCK',
                                    'NAME':f'GREEN_BLOCK_{i}',
                                    'ALIVE_STATUS':False,
                                    'X_SIZE':40,                                                #[S0]
                                    'Y_SIZE':40,                                                #[S0]
                                    'START_POSITION':(WINDOW_X_MAX,WINDOW_Y_MAX/2),
                                    'MOVE_STEP':-5,
                                    'IMAGE':pygame.image.load(IMAGE_PATH + 'greenBlock.png'),   #[S0]
                                    'IRO_BLOCK_LOAD_STATE':False,
                                    'CREATE_JIKANN':BLOCK_CREATE_TIME_LIST[i],    #   即绿色方块出现的时间点，该时间点相对于PYGAME_START_TIME，单位为s。
                                    
                                    }
        GREEN_BLOCK_DICT[str(i)] = IRO_BLOCK(GREEN_BLOCK_DICT[str(i)]['ID'],GREEN_BLOCK_DICT[str(i)]['TYPE'],GREEN_BLOCK_DICT[str(i)]['NAME'],\
                    GREEN_BLOCK_DICT[str(i)]['ALIVE_STATUS'],GREEN_BLOCK_DICT[str(i)]['X_SIZE'],GREEN_BLOCK_DICT[str(i)]['Y_SIZE'],\
                    GREEN_BLOCK_DICT[str(i)]['START_POSITION'],GREEN_BLOCK_DICT[str(i)]['MOVE_STEP'],GREEN_BLOCK_DICT[str(i)]['IMAGE'],\
                    BL_IRO_BLOCK_LOAD_STATE = GREEN_BLOCK_DICT[str(i)]['IRO_BLOCK_LOAD_STATE'],BLOCK_CREATE_JIKANN = GREEN_BLOCK_DICT[str(i)]['CREATE_JIKANN'])
# 以下两行为测试    def NEW_GREEN_BLOCK_OBJ(BLOCK_MAX_NUMBER)   函数用。
# NEW_GREEN_BLOCK_OBJ(GREEN_BLOCK_MAX_NUMBER)
# print("GREEN_BLOCK_DICT = ",GREEN_BLOCK_DICT,'\nGREEN_BLOCK_DICT类型为 ： ',type(GREEN_BLOCK_DICT),'\nGREEN_BLOCK_DICT个数为 ：',len(GREEN_BLOCK_DICT))

if __name__ == "__main__":
    game_init()
    NEW_GREEN_BLOCK_OBJ(GREEN_BLOCK_MAX_NUMBER)
    print("GREEN_BLOCK_DICT = ",GREEN_BLOCK_DICT,'\nGREEN_BLOCK_DICT类型为 ： ',type(GREEN_BLOCK_DICT),'\nGREEN_BLOCK_DICT个数为 ：',len(GREEN_BLOCK_DICT))

    PYGAME_START_TIME = time.time()
    print('游戏启动时间为 : ',time.ctime(PYGAME_START_TIME))
    SET_PYGAME_RUNNING_TIME = 80                                         #[S0]   设定pygame的运行时间，单位为s
    while time.time()-PYGAME_START_TIME < SET_PYGAME_RUNNING_TIME :      #   使用time来控制pygame的整体运行时间。
        refresh_pygame()
        GREEN_BLOCK_DICT['0'].load()    #[S0]   注意若用于显示Rect对象的blit()未写，而只写move_ip()，则在后台中，pygame.Rect()对象是依旧在移动的，但不会被显示出来。
        GREEN_BLOCK_DICT['0'].move()    #[S0]   故要放弃以前的写法，使用类似于本程序中在blit()的包裹函数load()和move()两个函数上动手脚，这样才能在规定时间里显示并移动，而不是 不显示并移动。
                                        #   综上2行，即pygame()将这两个东西分开的原因是，显示归显示(使用blit)，pyugame.Rect对象移动归移动(使用move_ip)，
                                        #   若要在显示的同时并移动，则必须blit和move_ip同时被调用才行。具体见self.IRO_BLOCK_LOAD_STATE这个参数出现的地方。
        GREEN_BLOCK_DICT['1'].load()    
        GREEN_BLOCK_DICT['1'].move()    

        GREEN_BLOCK_DICT['2'].load()    
        GREEN_BLOCK_DICT['2'].move()  
        pygame.display.update()
        print("GREEN_BLOCK_DICT['0']的生存状态 : ",GREEN_BLOCK_DICT['0'].ALIVE_STATUS)
        #[for 方案 START]
        # for i in range(0,GREEN_BLOCK_MAX_NUMBER):
        #     refresh_pygame()
        #     GREEN_BLOCK_DICT[str(i)].load()
        #     GREEN_BLOCK_DICT[str(i)].move()
        #     pygame.display.update()
        #print("GREEN_BLOCK_DICT['0']的生存状态 : ",GREEN_BLOCK_DICT['0'].ALIVE_STATUS)
        #[for 方案 END]
    print('游戏结束时间为 : ',time.ctime(time.time()))
    print("绿色方块的GREEN_BLOCK_DICT['0'].RECT.left = ",GREEN_BLOCK_DICT['0'].RECT.left)


    #   总结：
    #       之后就是和公司的项目结合起来，比如提前获取midi的note号出现的时间，然后因为
    #       是已知pygame窗口的长宽像素值，然后现在本文件又解决了 每多少秒移动多少STEP像素值的问题。
    #       故可以将note号提前多少秒 移动，然后到需要打击的时候，该绿色方块便移动到了pygame窗口的最左边之类的。
    #       双线程，移动归移动，放歌归放歌。

    #       即此处的[S0]和绿色方块的个数以及出现时间相关的参数可以被提前编辑好。
    #       然后main中的while也可以修改为歌曲时长。
    #       refresh_pygame()和pygame.display.update()之间的内容可以通过 for来进行(本文已存在for形式，虽然运行成功，但显示时好闪)，见[for 方案 START]~[END]
    #       所以改回了是用手打的方式来使用load和move这两个函数。
    #       故下一个版本中将使用pygame.sptite.Group进行解决，见_8_pygame_demo_color_dictWithClass_3_Multi_Group_ExOK.py。
    #       通过pygame.Sprite的Group来解决pygame.Rect的显示和移动问题，该方法见_4_pygame_demo_Part3_OK.py